#include "Image.h"

Image::Image(){
	collisionMap = NULL;
}

Image::Image(const char* filename, ColourKey key)
{
	printf("Loading %s\n", filename);

    // Load image into an SDL surface
    #if BUZZ_USE_SDL_IMAGE == 1
        SDL_Surface* surface = IMG_Load(filename);
    #else
        SDL_Surface* surface = SDL_LoadBMP(filename);
    #endif

    if(surface == NULL)
	{
		printf("Error: image %s not found\n", filename);
		return;
	}

    // Grab width and height
    width = surface->w;
    height = surface->h;



    // Convert to OpenGL texture
    GLfloat coords[4];
    texID = SDL_GL_LoadTexture(surface, coords, &key, NULL);

    // Setup opengl reference coordinates (between 0 and 1)
    texRefTop = 0;
    texRefBottom = coords[3];
    texRefLeft = 0;
    texRefRight = coords[2];

    // Initialise drawing reference point to top left of the image
    refX = refY = 0;

    // Default fully visible, no tints
    tintR = tintG = tintB = alpha = 255;

    // Default no rotation
    rotation = 0;

    // Default scaling is 1 (ie normal size)
    scale = 1;

    // Free original SDL surface from memory
    SDL_FreeSurface(surface);
}

Image::Image(SDL_Surface* surface, SDL_Rect* rect, ColourKey key)
{
    //assert(surface != NULL);

    // Grab width and height
    width = rect->w;
    height = rect->h;
    // Convert to OpenGL texture
    GLfloat coords[4];
    texID = SDL_GL_LoadTexture(surface, coords, &key, rect);

    // Setup opengl reference coordinates (between 0 and 1)
    texRefTop = 0;
    texRefBottom = coords[3];
    texRefLeft = 0;
    texRefRight = coords[2];

    // Initialise drawing reference point to top left of the image
    refX = refY = 0;

    // Default fully visible, no tints
    tintR = tintG = tintB = alpha = 255;

    // Default no rotation
    rotation = 0;

    // Default scaling is 1 (ie normal size)
    scale = 1;
}

Image::~Image() {
    //Deallocate opengl texture
	glDeleteTextures(1,&texID);

    //Release collision map
	if(collisionMap != NULL)
	{
		for(int i=0; i < width; i++) delete[] collisionMap[i];
		delete[] collisionMap;
	}
}

bool Image::collisionAt(int x, int y, bool flipped)
{
	if(x >= width || y>= height) return false;

	if(flipped) x = width-x-1;
	//y = height - y;

	return ( collisionMap[x][y] == 1 );
}

inline void Image::draw(int x, int y, bool flipped){
	SDL_Rect r;
	r.x = x;
	r.y = y;
	r.w = width;
	r.h = height;
	draw(&r, flipped, texRefLeft, texRefTop, texRefRight, texRefBottom);
}

void Image::draw(SDL_Rect* destination, SDL_Rect* source)
{
	draw(
		destination, false,
		(float) source->x / actualWidth,
		(float) source->y / actualHeight,
		(float)(source->x+source->w) / actualWidth,
		(float)(source->y+source->h) / actualHeight
	);
}

void Image::draw(SDL_Rect* destination, bool flipped, float uLeft, float vTop, float uRight, float vBottom)
{
   	float halfWidth = (float) destination->w / 2;
	float halfHeight = (float) destination->h / 2;

    // Set this image as opengl's current texture
	glBindTexture(GL_TEXTURE_2D, texID);

    // Reset opengl Matrix
    //glLoadIdentity();
    glPushMatrix();

    // Translate to reference point of the image
	glTranslatef((GLfloat)destination->x + halfWidth - refX, (GLfloat)destination->y + halfHeight - refY, 0.0f);

    if(rotation != 0.0f) glRotatef(rotation, 0.0f, 0.0f, 1.0f);

    glScalef(scale,scale,1);

    glColor4ub(tintR, tintG, tintB, alpha);

	if(flipped == false) {
		glBegin(GL_QUADS);
			glTexCoord2f(uLeft, vTop);
			glVertex2f((float)(0-halfWidth), (float)(0-halfHeight));
			glTexCoord2f(uRight, vTop);
			glVertex2f(halfWidth, (float)(0-halfHeight));
			glTexCoord2f(uRight, vBottom);
			glVertex2f(halfWidth, (float)ceilf(halfHeight));
			glTexCoord2f(uLeft, vBottom);
			glVertex2f((float)(0-halfWidth), (float)ceilf(halfHeight));
		glEnd();
	} else {
		glBegin(GL_QUADS);
			glTexCoord2f(uRight, vTop);
			glVertex2f((float)(0-halfWidth), (float)(0-halfHeight));
			glTexCoord2f(uLeft, vTop);
			glVertex2f((float)(0+halfWidth), (float)(0-halfHeight));
			glTexCoord2f(uLeft, vBottom);
			glVertex2f((float)(0+halfWidth), (float)(0+halfHeight));
			glTexCoord2f(uRight, vBottom);
			glVertex2f((float)(0-halfWidth), (float)(0+halfHeight));
		glEnd();
	}

    glPopMatrix();
    // Clear alpha and colour stuff
    glColor3ub(255,255,255);

    glBindTexture(GL_TEXTURE_2D, 0);
    glLoadIdentity();
}

GLuint Image::SDL_GL_LoadTexture(SDL_Surface *surface, GLfloat *texcoord, ColourKey* key, SDL_Rect* srcRect)
{
	GLuint texture;
	int w, h;
	SDL_Surface *image;
	Uint32 saved_flags;
	Uint8  saved_alpha;


	texcoord[0] = 0.0f;			/* Min X */
	texcoord[1] = 0.0f;			/* Min Y */


	/* Use the surface width and height expanded to powers of 2 */
	if(srcRect != NULL){
		w = power_of_two(srcRect->w);
		h = power_of_two(srcRect->h);
		texcoord[2] = (GLfloat)srcRect->w / w;	/* Max X */
        texcoord[3] = (GLfloat)srcRect->h / h;	/* Max Y */
	}else{
		w = power_of_two(surface->w);
		h = power_of_two(surface->h);
		texcoord[2] = (GLfloat)surface->w / w;	/* Max X */
        texcoord[3] = (GLfloat)surface->h / h;	/* Max Y */
	}

    // Record actual width and height of the image
    actualWidth = w;
    actualHeight = h;

    // Create a new surface compatible with OpenGL
	image = SDL_CreateRGBSurface(
        SDL_SWSURFACE,
        w, h, 32,
        SDL_RMASK, SDL_GMASK, SDL_BMASK, SDL_AMASK
    );

	if ( image == NULL ) return 0;

	/* Save the alpha blending attributes */
	saved_flags = surface->flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
	saved_alpha = surface->format->alpha;
	if ( (saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) {
		SDL_SetAlpha(surface, 0, 0);
	}

    // Apply colour key before copying surface
	if( !key->empty ){
        // Create key from components
        Uint32 key32 = SDL_MapRGB(surface->format, key->r, key->g, key->b);
		SDL_SetColorKey(surface, SDL_SRCCOLORKEY, key32);
    }

    // Blit the original surface to the OpenGL surface
	SDL_BlitSurface(surface, srcRect, image, NULL);

	SDL_LockSurface(image);

	collisionMap = NULL;
    collisionMap = new byte*[image->w];

    for(int x=0; x< width; x++)
    {
        collisionMap[x] = new byte[height];

		for(int y=0; y<height; y++)
        {
			Uint32 c;
			byte* pixels = (byte*) image->pixels;
			pixels += image->pitch * y;
			pixels += 4 * x;

			memcpy(&c, pixels, 4);

			collisionMap[x][y] = (c & SDL_AMASK) ? 1 : 0;
        }
    }

	SDL_UnlockSurface(image);

	/*for(int y=0; y < height; y++)
	{
		for(int x=0; x < width; x++){
			printf("%d", collisionMap[x][y]);
		}
		printf("\n");
	}
	flushall();*/


	/* Restore the alpha blending attributes */
	if ( (saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) {
		SDL_SetAlpha(surface, saved_flags, saved_alpha);
	}

	/* Create an OpenGL texture for the image */
	glGenTextures(1, &texture);
	glBindTexture(GL_TEXTURE_2D, texture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexImage2D(
		GL_TEXTURE_2D,
	     0,
	     GL_RGBA,
	     w, h,
	     0,
	     GL_RGBA,
	     GL_UNSIGNED_BYTE,
	     image->pixels
	);

	// Free SDL surface
	SDL_FreeSurface(image);

	return texture;
}

int Image::getWidth(){
	return width;
}

int Image::getHeight()
{
	return height;
}
